class: title-slide # ER018 - Analyzing Business Relations & Documents ## PVA1 ### Quantitative Textanalyse mit tidytext <br> <br> <br> <br> <br> <br> <br> ### FS 2024 <br> ### Prof. Dr. Jörg Schoder .mycontacts[
@FFHS-EconomicResearch
@jfschoder ] --- layout: true <div class="my-footer"></div> <div style="position: absolute;left:400px;bottom:10px;font-size:9px">
Prof. Dr. Jörg Schoder</div> --- name: agenda class: left .blockquote[Agenda] ## Quantitative Textanalyse mit tidytext * Datenstrukturen zur Speicherung von Textdaten * tidytext und Tokenisierung * Wort- und Textmetriken * Sentimentanalysen * Beziehungen zwischen Wörtern * NLP und Topic Modeling --- class: left .blockquote[Datenstrukturen Textdaten] ## Formen zur Speicherung von Textdaten * **strings**: Speichern von Text als Zeichenvektor; typischerweise werden Textdaten in dieser Form in R eingelesen. * **Corpus**: Objektyp enthält meist rohe strings, die mit **zusätzlichen Metadaten** und Details ergänzt sind. * **Dokument-Begriffs-Matrix**: * Sammlung (Korpus) von Dokumenten mit * einer Zeile für jedes Dokument * einer Spalte für jeden Begriff * Wert in der Matrix ist in der Regel die Wortanzahl oder tf-idf * **tidy text**: angelehnt an tidy-Datenformat: ein Token pro Zeile ??? * **tf-idf** soll messen, **wie wichtig ein Wort** für ein Dokument in einer Sammlung (oder einem Korpus) von Dokumenten ist, zum Beispiel für einen Roman in einer Sammlung von Romanen oder für eine Website in einer Sammlung von Websites. * tf: term frequency * idf: inverse term frequency --- class: left .blockquote[Datenstrukturen Textdaten] ## Beispiel Airbnb Zürich .panelset[ .panel[.panel-name[Daten importieren] ```r # Loading data from data folder ----- my_in_file <- "airbnbZH_20240408.rds" tbl_airbnbZH <- readr::read_rds(xfun::from_root('data','raw',my_in_file)) head(tbl_airbnbZH) ``` ``` ## # A tibble: 6 × 12 ## listing_id id date reviewer_id reviewer_name comments host_id ## <int64> <int64> <IDate> <int> <chr> <chr> <int> ## 1 3.62e-319 6.45e-318 2012-05-19 1787723 Jeff "Simona was… 377532 ## 2 3.62e-319 7.16e-318 2012-06-10 2183393 Rick "The apartm… 377532 ## 3 3.62e-319 7.78e-318 2012-06-27 2343568 Joseph "I stayed i… 377532 ## 4 3.62e-319 8.62e-318 2012-07-19 2343192 Yvonne "The apartm… 377532 ## 5 3.62e-319 1.00e-317 2012-08-19 2092762 Joy "Simona's p… 377532 ## 6 3.62e-319 1.08e-317 2012-09-03 3386921 Petru "Dear Simon… 377532 ## # ℹ 5 more variables: property_type <chr>, room_type <chr>, priceUSD <dbl>, ## # accommodates <int>, rating <dbl> ``` ] .panel[.panel-name[Strukturierte Daten] ```r tbl_airbnbZH %>% dplyr::filter(priceUSD<=100) %>% dplyr::summarise(mean_rating=mean(rating, na.rm = TRUE)) ``` ``` ## # A tibble: 1 × 1 ## mean_rating ## <dbl> ## 1 4.73 ``` ] .panel[.panel-name[Unstrukturierte Daten] ```r tbl_airbnbZH %>% dplyr::filter(priceUSD<=100) %>% dplyr::summarise(mean_rating=mean(comments)) ``` ``` ## Warning: There was 1 warning in `dplyr::summarise()`. ## ℹ In argument: `mean_rating = mean(comments)`. ## Caused by warning in `mean.default()`: ## ! Argument ist weder numerisch noch boolesch: gebe NA zurück ``` ``` ## # A tibble: 1 × 1 ## mean_rating ## <dbl> ## 1 NA ``` ] ] --- class: left .blockquote[Datenstrukturen Textdaten] ## Spalten und Vektortypen am Beispiel Airbnb ZH .panelset[ .panel[.panel-name[Überblick] ```r tbl_airbnbZH %>% dplyr::glimpse() ``` ``` ## Rows: 79,596 ## Columns: 12 ## $ listing_id <int64> 3.620612e-319, 3.620612e-319, 3.620612e-319, 3.620612e… ## $ id <int64> 6.446667e-318, 7.158324e-318, 7.779232e-318, 8.624267e… ## $ date <IDate> 2012-05-19, 2012-06-10, 2012-06-27, 2012-07-19, 2012-0… ## $ reviewer_id <int> 1787723, 2183393, 2343568, 2343192, 2092762, 3386921, 39… ## $ reviewer_name <chr> "Jeff", "Rick", "Joseph", "Yvonne", "Joy", "Petru", "Mar… ## $ comments <chr> "Simona was an excellent hostess. The apartment is beaut… ## $ host_id <int> 377532, 377532, 377532, 377532, 377532, 377532, 377532, … ## $ property_type <chr> "Entire rental unit", "Entire rental unit", "Entire rent… ## $ room_type <chr> "Entire home/apt", "Entire home/apt", "Entire home/apt",… ## $ priceUSD <dbl> 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1… ## $ accommodates <int> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,… ## $ rating <dbl> 4.78, 4.78, 4.78, 4.78, 4.78, 4.78, 4.78, 4.78, 4.78, 4.… ``` ] .panel[.panel-name[numeric] ```r tbl_airbnbZH %>% dplyr::select(listing_id,priceUSD) %>% head() ``` ``` ## # A tibble: 6 × 2 ## listing_id priceUSD ## <int64> <dbl> ## 1 3.62e-319 100 ## 2 3.62e-319 100 ## 3 3.62e-319 100 ## 4 3.62e-319 100 ## 5 3.62e-319 100 ## 6 3.62e-319 100 ``` ] .panel[.panel-name[character] ```r tbl_airbnbZH %>% dplyr::select(comments) %>% head() ``` ``` ## # A tibble: 6 × 1 ## comments ## <chr> ## 1 "Simona was an excellent hostess. The apartment is beautiful (and exactly lik… ## 2 "The apartment was fantastic - clean, beautiful and good location. Simona was… ## 3 "I stayed in the apartment for two weeks with my wife and daughter. The apart… ## 4 "The apartment was everything and more. spacious, clean, new! It had an eleva… ## 5 "Simona's place is great! It's very quiet and although not right downtown, i… ## 6 "Dear Simona, you are a splendid guest and the apartment was better that you … ``` ] ] ??? * numeric * integer (bspw. listing_id) * double (bspw. priceUSD) * character (bspw. comment) --- class: left .blockquote[Datenstrukturen Textdaten] ## Zusammenfassende Statistiken am Beispiel Airbnb ZH .panelset[ .panel[.panel-name[Variante 1] Kombination von `summarize()`- und `n()`-Funktion ```r tbl_airbnbZH %>% dplyr::group_by(property_type) %>% dplyr::summarize(n_zeilen=dplyr::n()) %>% dplyr::slice(1:4) ``` ``` ## # A tibble: 4 × 2 ## property_type n_zeilen ## <chr> <int> ## 1 Barn 59 ## 2 Camper/RV 1 ## 3 Casa particular 38 ## 4 Entire condo 2354 ``` ] .panel[.panel-name[Variante 2] `count()`-Funktion ```r tbl_airbnbZH %>% dplyr::count(property_type) %>% head() ``` ``` ## # A tibble: 6 × 2 ## property_type n ## <chr> <int> ## 1 Barn 59 ## 2 Camper/RV 1 ## 3 Casa particular 38 ## 4 Entire condo 2354 ## 5 Entire guest suite 583 ## 6 Entire guesthouse 178 ``` ] .panel[.panel-name[Sortierung] `arrange()`-Funktion ```r tbl_airbnbZH %>% dplyr::count(property_type) %>% dplyr::arrange(desc(n)) %>% dplyr::slice(1:4) ``` ``` ## # A tibble: 4 × 2 ## property_type n ## <chr> <int> ## 1 Entire rental unit 46681 ## 2 Private room in rental unit 14512 ## 3 Private room in home 3658 ## 4 Entire condo 2354 ``` ] ] --- class: inverse, center, middle ## Tokenisierung und Textmetriken .blockquote[tidytext-Format und Tokenisierung] .blockquote[Worthäufigkeiten] .blockquote[Datenvisualisierung] --- class: left .blockquote[tidytext-Format und Tokenisierung] ## tidy-Format und tidyverse .panelset[ .panel[.panel-name[tidyformat] * tidy-Daten <img src="data:image/png;base64,#../../img/PVA1/tidy-data_(Wickham).png" width="70%" style="display: block; margin: auto;" /> * Vorteil einheitlicher Struktur von Daten: überschaubare Zahl an (zu erlernenden) Werkzeugen und Verfahren. .quellePanURL[Bildquelle: [Wickham et al. (2023)](wickham_r_2023).] ] .panel[.panel-name[tidyverse] *
als Sprache, tidy als Dialekt * tidyverse als "Metapaket" ```r library(tidyverse) ``` ``` ## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ── ## ✔ dplyr 1.1.2 ✔ readr 2.1.4 ## ✔ forcats 1.0.0 ✔ stringr 1.5.1 ## ✔ ggplot2 3.4.2 ✔ tibble 3.2.1 ## ✔ lubridate 1.9.3 ✔ tidyr 1.3.0 ## ✔ purrr 1.0.2 ## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ── ## ✖ tidyr::extract() masks magrittr::extract() ## ✖ dplyr::filter() masks stats::filter() ## ✖ dplyr::lag() masks stats::lag() ## ✖ purrr::set_names() masks magrittr::set_names() ## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors ``` ] .panel[.panel-name[Kernelemente] * tidy-Daten * jede Variable eine Spalte darstellt, * jeder Fall eine Zeile und * jeder Wert einer Beobachtung entspricht. * Verben (dplyr-Bibliothek) * Spaltenbezogene Operationen (bspw. `select()`, `mutate()`) * Zeilenbezogene Operationen (bspw. `filter()`, `arrange()`,`slice()`) * Operationen für Gruppen von Zeilen (bspw. `group_by()`, `summarize()`) * Pipe (`%>%`)
Ausführlicher im Skript [Datenprojekte in R](https://ffhs-economicresearch.github.io/ER018/Rmd/PVA1/04_DatenprojekteR.html#1) ] ] ??? * tidy-Daten: * jede Variable eine Spalte darstellt, * jeder Fall eine Zeile und * jeder Wert einer Beobachtung entspricht. * tidyverse-Bibliotheken... * ...decken grundsätzlich alle notwendigen Schritte eines Datenprojekts ab * ...und sind Verwendung mit tidy Daten konzipiert/optimiert. vgl. ausführlicher [Intro Datenprojekte in R](https://ffhs-economicresearch.github.io/ER018/Rmd/PVA1/04_DatenprojekteR.html#1) --- class: left .blockquote[tidytext-Format und Tokenisierung] ## Das tidytext-Paket <iframe src="https://www.tidytextmining.com/" width="100%" height="410px" data-external="1"></iframe> <a name=cite-silge_text_2017></a>[Silge and Robinson (2017)](#bib-silge_text_2017) --- class: left .blockquote[tidytext-Format und Tokenisierung] ## Tokenisierung .panelset[ .panel[.panel-name[tidytext-Format] .blockquote[ "We [..] define the tidy text format as being a table with **one-token-per-row**." .tr[ [Silge and Robinson (2017)](#bib-silge_text_2017) ] ] .blockquote[ "A **token** is a **meaningful unit of text**, most often a word, that we are interested in using for further analysis, and tokenization is the process of splitting text into tokens." .tr[ [Silge and Robinson (2017)](#bib-silge_text_2017) ] ] ] .panel[.panel-name[tidytext-Funktion] * `unnest_tokens()`-Funktion als komfortable Möglichkeit zur Tokenisierung in der tidytext-Bibliothek * Argumente: * input * output * **token** * format ("text", "man", "latex", "html", "xml") * to_lower * ... * Option **token**: * "characters" * "regex" * "words" * "sentences" * "lines" * "paragraphs" * ... ] ] ??? * A **token** is a meaningful unit of text, most often a word, that we are interested in using for further analysis, and tokenization is the process of splitting text into tokens --- class: left .blockquote[tidytext-Format und Tokenisierung] ## Beispiel Tokenisierung .panelset[ .panel[.panel-name[Sätze] ```r library(tidytext) tidy_satz <- tbl_airbnbZH %>% unnest_tokens(input = "comments",output = "satz", token="sentences") tidy_satz %>% select(listing_id,id,reviewer_name,satz) %>% slice(1:4) ``` ``` ## # A tibble: 4 × 4 ## listing_id id reviewer_name satz ## <int64> <int64> <chr> <chr> ## 1 3.62e-319 6.45e-318 Jeff simona was an excellent hostess. ## 2 3.62e-319 6.45e-318 Jeff the apartment is beautiful (and exactly li… ## 3 3.62e-319 6.45e-318 Jeff simona made sure the apartment was well st… ## 4 3.62e-319 6.45e-318 Jeff at one point the internet went out, and si… ``` ] .panel[.panel-name[Wörter] ```r tidy_wort <- tbl_airbnbZH %>% mutate(id = row_number()) %>% unnest_tokens(input = "comments",output = "wort", token = "words") tidy_wort %>% select(listing_id,id,reviewer_name,wort) %>% slice(1:5) ``` ``` ## # A tibble: 5 × 4 ## listing_id id reviewer_name wort ## <int64> <int> <chr> <chr> ## 1 3.62e-319 1 Jeff simona ## 2 3.62e-319 1 Jeff was ## 3 3.62e-319 1 Jeff an ## 4 3.62e-319 1 Jeff excellent ## 5 3.62e-319 1 Jeff hostess ``` ] .panel[.panel-name[Bereinigung] * Eliminierung von Füllwörtern unter Rückgriff auf Datenbanken der **stopwords**-Bibliothek * Unterstützte Sprachen: "en", "es", "de", "fr" * Datenkombination mit `anti_join()`-Funktion (vgl. [cheat sheet](https://github.com/rstudio/cheatsheets/blob/main/data-transformation.pdf)) ```r tidy_wort <- tidy_wort %>% anti_join(get_stopwords(language = "en", source = "snowball" ), join_by(wort == word) ) ``` ] .panel[.panel-name[tidy Wörter] ```r tidy_wort %>% select(listing_id,id,reviewer_name,wort) %>% slice(1:7) ``` ``` ## # A tibble: 7 × 4 ## listing_id id reviewer_name wort ## <int64> <int> <chr> <chr> ## 1 3.62e-319 1 Jeff simona ## 2 3.62e-319 1 Jeff excellent ## 3 3.62e-319 1 Jeff hostess ## 4 3.62e-319 1 Jeff apartment ## 5 3.62e-319 1 Jeff beautiful ## 6 3.62e-319 1 Jeff exactly ## 7 3.62e-319 1 Jeff like ``` ] ] ??? * Datenbank `stop_words` in tidytext-Bibliothek enthalten * Kombination der beiden Datensätze `tidy_wort` und `stop_words` mit der `anti_join()`-Funktion elminiert alle Füllwörter. * Obs! `tbl_airbnbZH` enthalt auch andere Sprachen als Englisch! * stopwords-Bibliothek unterstützt zwar mehrere Sprachen, aber die Bereinigung muss einzeln für jede Sprache durchgeführt werden. --- class: left .blockquote[tidytext-Format und Tokenisierung] ## Erste (primitive) quantitative Auswertung .panelset[ .panel[.panel-name[Balkendiagramm] <!-- --> ``` ## $y ## NULL ## ## attr(,"class") ## [1] "labels" ``` ] .panel[.panel-name[Code zum Plot] ```r p <- tidy_wort %>% count(wort, sort = TRUE) %>% slice(1:20) %>% mutate(wort = reorder(wort, n)) %>% ggplot(aes(x=wort, y=n)) + geom_col() + coord_flip() labs(y = NULL) ``` ``` ## $y ## NULL ## ## attr(,"class") ## [1] "labels" ``` ] ] --- class: inverse, center, middle ## Wort- und Textmetriken .blockquote[Häufigkeiten] .blockquote[Zusammenhänge] --- class: left .blockquote[Häufigkeiten] ## Customizing "stop words" .panelset[ .panel[.panel-name[Ergänzung Liste] ```r custom_stop_words <- tribble( ~word, ~lexicon, "zurich", "CUSTOM", "br", "CUSTOM", "und", "CUSTOM", "sehr", "CUSTOM", ) stop_words2 <- get_stopwords(language = "en", source = "snowball") %>% bind_rows(custom_stop_words) ``` ] .panel[.panel-name[Bereinigung Teil2] ```r tidy_wort2 <- tidy_wort %>% anti_join(stop_words2, join_by(wort==word)) tidy_wort2 %>% count(wort, sort = TRUE) %>% slice(1:20) %>% mutate(wort = reorder(wort, n)) %>% ggplot(aes(x=wort, y=n)) + geom_col() + coord_flip() ``` <!-- --> ```r labs(y = NULL) ``` ``` ## $y ## NULL ## ## attr(,"class") ## [1] "labels" ``` ] ] ??? * die: deutsches Füllwort oder englisch: sterben? --- class: left .blockquote[Häufigkeiten] ## Wortlänge --- class: left .blockquote[Häufigkeitsanalysen] ## Satzlänge --- class: left .blockquote[Datenvisualisierung] ## Balkendiagramme ```r #Farben definieren ffhs_red <- "#d50006" ffhs_pur <- "#502479" my_cols <- c(ffhs_red,ffhs_pur) ``` --- class: left .blockquote[Datenvisualisierung] ## Wordcloud --- class: inverse, center, middle ## Sentimentanalysen .blockquote[Sentiment-Lexika] .blockquote[Beispiel] .blockquote[Datenvisualisierung] --- class: left .blockquote[Sentimentanalysen mit tidytext] ## Workflow <img src="data:image/png;base64,#../../img/PVA1/sentiment_tidytext_(Silge_Robinson_2017).png" width="100%" style="display: block; margin: auto;" /> .quelle[Quelle: [Silge and Robinson (2017)](#bib-silge_text_2017)] --- class: left .blockquote[Sentiment-Lexika] ## Beispiele einfach verfügbarer Sentiment-Lexika .panelset[ .panel[.panel-name[bing] ```r get_sentiments("bing") ``` ``` ## # A tibble: 6,786 × 2 ## word sentiment ## <chr> <chr> ## 1 2-faces negative ## 2 abnormal negative ## 3 abolish negative ## 4 abominable negative ## 5 abominably negative ## 6 abominate negative ## 7 abomination negative ## 8 abort negative ## 9 aborted negative ## 10 aborts negative ## # ℹ 6,776 more rows ``` ] .panel[.panel-name[afinn] ```r get_sentiments("afinn") ``` ``` ## # A tibble: 2,477 × 2 ## word value ## <chr> <dbl> ## 1 abandon -2 ## 2 abandoned -2 ## 3 abandons -2 ## 4 abducted -2 ## 5 abduction -2 ## 6 abductions -2 ## 7 abhor -3 ## 8 abhorred -3 ## 9 abhorrent -3 ## 10 abhors -3 ## # ℹ 2,467 more rows ``` ] .panel[.panel-name[loughran] ```r get_sentiments("loughran") ``` ``` ## # A tibble: 4,150 × 2 ## word sentiment ## <chr> <chr> ## 1 abandon negative ## 2 abandoned negative ## 3 abandoning negative ## 4 abandonment negative ## 5 abandonments negative ## 6 abandons negative ## 7 abdicated negative ## 8 abdicates negative ## 9 abdicating negative ## 10 abdication negative ## # ℹ 4,140 more rows ``` ] .panel[.panel-name[nrc] ```r get_sentiments("nrc") ``` ``` ## # A tibble: 13,872 × 2 ## word sentiment ## <chr> <chr> ## 1 abacus trust ## 2 abandon fear ## 3 abandon negative ## 4 abandon sadness ## 5 abandoned anger ## 6 abandoned fear ## 7 abandoned negative ## 8 abandoned sadness ## 9 abandonment anger ## 10 abandonment fear ## # ℹ 13,862 more rows ``` ] ] --- class: left .blockquote[Sentiment Lexika] ## Beispiel Loughran Dictionary .panelset[ .panel[.panel-name[Dataviz] ```r #|echo: false get_sentiments("loughran") %>% count(sentiment) %>% mutate(sentiment2 = fct_reorder(sentiment, n)) %>% ggplot(aes(x = sentiment2, y = n)) + geom_col() + coord_flip() + labs( title = "Sentiment Häufigkeiten (Loughran)", x = "Häufigkeit", y = "Sentiment" ) ``` <!-- --> ] `.panel[.panel.name[Code] ```r sent_counts <- get_sentiments("loughran") %>% count(sentiment) %>% mutate(sentiment2 = fct_reorder(sentiment, n)) %>% ggplot(aes(x = sentiment2, y = n)) + geom_col() + coord_flip() + labs( title = "Sentiment Häufigkeiten (Loughran)", x = "Häufigkeit", y = "Sentiment" ) ``` ] ] --- class: left .blockquote[Beispiel Airbnb ZH] ## Zuordnung: Sentiment der Kommentar-Wörter * Datenkombination mit der `inner_join()`-Funktion (vgl. [cheat sheet](https://github.com/rstudio/cheatsheets/blob/main/data-transformation.pdf)). ```r tbl_sentZH <- tidy_wort %>% inner_join(get_sentiments("bing"), join_by(wort == word), relationship = "many-to-many") %>% mutate(line = row_number()) %>% count(property_type, index = line %/% 80, sentiment) %>% pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>% mutate(sentiment = positive - negative) ``` --- class: left .blockquote[Datenvisualisierung] ## Beispiel: Airbnb Zürich Sentimentanalyse nach Art der Unterkunft ```r #Häufigkeiten unterschiedlicher Arten von Unterkünften tbl_props <- tbl_sentZH %>% count(property_type) %>% arrange(desc(n)) tbl_props ``` ``` ## # A tibble: 35 × 2 ## property_type n ## <chr> <int> ## 1 Entire rental unit 2598 ## 2 Private room in rental unit 914 ## 3 Entire condo 203 ## 4 Private room in home 180 ## 5 Entire loft 168 ## 6 Entire serviced apartment 165 ## 7 Room in hotel 88 ## 8 Private room in condo 56 ## 9 Private room in villa 42 ## 10 Private room in townhouse 39 ## # ℹ 25 more rows ``` ```r # Auswahl Top4 für die Grafik prop_list <- tbl_props %>% slice(1:4) %>% pull(property_type) tbl_plot <- tbl_sentZH %>% filter(property_type%in%prop_list) # Erzeugung ggplot (speichern im Objekt p) p <- tbl_plot %>% ggplot(aes(index, sentiment, fill = property_type)) + geom_col(show.legend = FALSE) + facet_wrap(vars(property_type), ncol = 2, scales = "free_x") ``` --- class: left .blockquote[Datenvisualisierung] ## Balkendiagramm Sentimentanalyse Airbnb ZH <!-- --> --- class: inverse, center, middle ## Topic Modeling .blockquote[Grundlegende NLP-Begriffe] .blockquote[LDA] --- class: left .blockquote[Grundlegende NLP-Begriffe] ## Unsupervised Learning * Latente Dirichlet-Zuordnung (LDA): Standard-Themenmodell (Topic Model) -- * Korpus: Sammlung von Dokumenten -- * Bag-of-Words: jedes Wort in einem Dokument wird separat/isoliert behandelt -- * Themenmodelle finden **Muster zusammen vorkommenender Wörter** -- * Unsupervised Learning: Suche nach Mustern anstelle von Vorhersagen wird als bezeichnet. --- class: left .blockquote[Grundlegende NLP-Begriffe] ## Themen (Topics) als gruppierte Listen * Themen-/Kategorienbildung nach Wahrscheinlichkeiten, mit denen ein Wort in einem Thema vorkommt * Fuzzy-Logik vs. binäre Logik * Fuzzy: ein Wort gehört mit einer bestimmten Wahrscheinlichkeit zu einem Thema * binäre Logik: ein Wort gehört entweder zu einem Thema, oder nicht * Thema als Liste aller Wörter im Korpus. Jedem Wort ist eine Wahrscheinlichkeit zugewiesen * Wörter, die häufig gemeinsam vorkommen, gehören mit größerer Wahrscheinlichkeit zu einem Thema --- class: left .blockquote[Grundlegende NLP-Begriffe] ## Topic Modeling vs. Clusteranalyse .pull-left[ **Clusteranalyse** - Exploratives Verfahren für quantitative Daten - jede Einheit gehört zu einem Cluster oder nicht (binäre Logik) - verschieden Algorithmen (bspw. k-means) - Anzahl Cluster nicht vorab festgelegt ] -- .pull-right[ **Topic Modeling** - Exploratives Verfahren für (qualitative) Textdaten - jedes Dokument (bspw. Kundenkommentar, Buch...) kann zu mehreren Themen gehören (partielles Mitglied, fuzzy-Logik) - verschieden Algorithmen (bspw. Gibbs) - Anzahl Themen nicht vorab festgelegt ] --- class: left .blockquote[LDA] --- name: EndHanks class: center background-size: 75% background-image: url(data:image/png;base64,#https://media.giphy.com/media/KJ1f5iTl4Oo7u/giphy.gif) --- class: left ## Quellenverzeichnis .ref-slide[ <a name=bib-silge_text_2017></a>[Silge, J. and D. Robinson](#cite-silge_text_2017) (2017). _Text Mining with R: A Tidy Approach_. First edition. Beijing Boston Farnham Sebastopol Tokyo: O'Reilly. ISBN: 978-1-4919-8165-8. <a name=bib-wickham_r_2023></a>[Wickham, H., M. Çetinkaya-Rundel, and G. Grolemund](#cite-wickham_r_2023) (2023). _R for Data Science: Import, Tidy, Transform, Visualize, and Model Data_. 2nd Edition. Beijing Boston Farnham Sebastopol Tokyo: O'Reilly. ISBN: 978-1-4920-9740-2. ]